<?php
namespace  App\Wolf\Pay;

class AlipayPc 
{
    public $partner; //商户号
    //收款支付宝账号，以2088开头由16位纯数字组成的字符串，一般情况下收款账号就是签约账号
    public $seller_id;
    // MD5密钥，安全检验码，由数字和字母组成的32位字符串，查看地址：https://b.alipay.com/order/pidAndKey.htm
    public $key;
    // 服务器异步通知页面路径  需http://格式的完整路径，不能加?id=123这类自定义参数，必须外网可以正常访问
    public $notify_url;
    // 页面跳转同步通知页面路径 需http://格式的完整路径，不能加?id=123这类自定义参数，必须外网可以正常访问
    public $return_url;
    //签名方式
    public $sign_type     = strtoupper('MD5');
    //字符编码格式 目前支持 gbk 或 utf-8
    public $input_charset = strtolower('utf-8');
    //暂时不确定用不用的到
    public $cacert        = getcwd().'\\cacert.pem';

    //访问模式,根据自己的服务器是否支持ssl访问，若支持请选择https；若不支持请选择http
    public $transport     = 'http';

    // 支付类型 ，无需修改
    public $payment_type  = "1";

    // 产品类型，无需修改
    public $service       = "create_direct_pay_by_user";

    //↓↓↓↓↓↓↓↓↓↓ 请在这里配置防钓鱼信息，如果没开通防钓鱼功能，为空即可 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
            
    // 防钓鱼时间戳  若要使用请调用类文件submit中的query_timestamp函数
    public $anti_phishing_key  = ""; //暂不支持
            
        // 客户端的IP地址 非局域网的外网IP地址，如：221.0.0.1
    public $exter_invoke_ip    = "";

    /**
     *支付宝网关地址（新）
     */
    public $alipay_gateway_new = 'https://mapi.alipay.com/gateway.do?';

    /**
     * HTTPS形式消息验证地址
     */
    public $https_verify_url = 'https://mapi.alipay.com/gateway.do?service=notify_verify&';
    /**
     * HTTP形式消息验证地址
     */
    public $http_verify_url = 'http://notify.alipay.com/trade/notify_query.do?';



    /**
     * [配置参数]
     * @param public_key    支付宝公钥
     * @param private_key   商户私钥
     * @param appid         应用appid
     * @param notify_url    异步回调
     */
    public function __construct($data) 
    {
        $this->partner     = $data['partner'];
        $this->key         = $data['key'];
        $this->notify_url  = $data['notify_url'];
        $this->return_url  = $data['return_url'];  
    
    }
    /**
     * 生成支付html 跳转收银台
     * @param  [array] $data  [业务参数]
     * @return [type] [description]
     */
    public function pay($data)
    {
        //获得基础参数
        $params = $this->getConfig();
        //业务参数
        $params['out_trade_no'] = $data['out_trade_no'];//商户订单号，
        $params['subject']      = $data['subject'];//订单名称，必填
        $params['total_fee']    = $data['total_fee'];//付款金额，必填
        $params['body']         = $data['body'];//商品描述，可空
        //构造html提交 表单  默认为get 方法
        $html = $this->buildRequestForm($params);
        echo $html;
        die;
    }

    /**
     * 支付回调
     * @param [array] $data  [全部回调参数]
     * @return [bool] true/false
     */
    public function notifyCheck($data) 
    {
        if (empty($data)) return false;

        //除去待签名参数数组中的空值和签名参数
        $params = array();
        foreach ($data as $key => $value) {

            if($key == "sign" || $key == "sign_type" || $val == "")continue;
            else    $params[$key] = $val;
        }

        //核对签名结果
        $isSign = $this->getSignVeryfy($data, $data["sign"]);
        if ($isSign == false) {
            echo  '签名验证失败';
            die;
        }
        //获取支付宝远程服务器ATN结果（验证是否是支付宝发来的消息）
        $responseTxt = 'false';
        if (! empty($data["notify_id"])) {
            $responseTxt = $this->getResponse($data["notify_id"]);
        }

        return preg_match("/true$/i",$responseTxt) ? true : false;
    }




    /**
     * 获得配置参数数据 (基础参数)
     * @return [type] [description]
     */
    private function getConfig() 
    {
        $config = array(
            "service"           => $this->service,
            "partner"           => $this->partner,
            "seller_id"         => $this->seller_id,
            "_input_charset"    => $this->input_charset,
            "notify_url"        => $this->notify_url,
            "return_url"        => $this->return_url,
            "payment_type"      => $this->payment_type,
            "anti_phishing_key" =>$this->anti_phishing_key,
            "exter_invoke_ip"   =>$this->exter_invoke_ip,
        );

        return $config;
    }

     /**
     * 建立请求，以表单HTML形式构造（默认）
     * @param $para_temp 请求参数数组
     * @param $method 提交方式。两个值可选：post、get
     * @param $button_name 确认按钮显示文字
     * @return 提交表单HTML文本
     */
    private function buildRequestForm($para_temp, $method = 'get') {
        //待请求参数数组
        $para = $this->buildRequestPara($para_temp);
        
        $sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='".$this->alipay_gateway_new."_input_charset=".$this->input_charset."' method='".$method."'>";
        while (list ($key, $val) = each ($para)) {
            $sHtml.= "<input type='hidden' name='".$key."' value='".$val."'/>";
        }

        //submit按钮控件请不要含有name属性
        $sHtml = $sHtml."<input type='submit'  value='提交' style='display:none;'></form>";
        
        $sHtml = $sHtml."<script>document.forms['alipaysubmit'].submit();</script>";
        
        return $sHtml;
    }

    /**
     * 生成要请求给支付宝的参数数组
     * @param $para_temp 请求前的参数数组
     * @return 要请求的参数数组
     */
    private function buildRequestPara($para_temp) {
        //除去待签名参数数组中的空值和签名参数
        $params = array();
        while (list ($key, $val) = each ($para_temp)) {
            if($val != "")
                $params[$key] = $val;
        }


        //生成签名结果
        $mysign = $this->buildRequestMysign($params);
        
        //签名结果与签名方式加入请求提交参数组中
        $params['sign']      = $mysign;
        $params['sign_type'] = $this->sign_type;
        
        return $params;
    }

 

    /**
     * 生成签名结果
     * @param $para_sort 已排序要签名的数组
     * return 签名结果字符串
     */
    private function buildRequestMysign($para_sort) {
        //把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        $prestr = $this->createLinkstring($para_sort);
        
        $mysign = "";
        switch ($this->sign_type) {
            case "MD5" :
                $mysign = md5($prestr.$this->key);
                break;
            default :
                $mysign = "";
        }
        
        return $mysign;
    }

    /**
     * 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
     * @param $para 需要拼接的数组
     * return 拼接完成以后的字符串
     */
    private function createLinkstring($para) {

        //对待签名参数数组排序
        ksort($params);
        reset($params);

        $arg  = "";
        while (list ($key, $val) = each ($para)) {
            $arg.=$key."=".$val."&";
        }
        //去掉最后一个&字符
        $arg = substr($arg,0,count($arg)-2);
        
        //如果存在转义字符，那么去掉转义
        if(get_magic_quotes_gpc()){$arg = stripslashes($arg);}
        
        return $arg;
    }

        /**
     * 获取返回时的签名验证结果
     * @param $data 通知返回来的参数数组
     * @param $sign 返回的签名结果
     * @return 签名验证结果
     */
    private function getSignVeryfy($data, $sign) {
        
        //把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        $prestr = $this->createLinkstring($params);
        
        $isSgin = false;
        switch ($this->sign_type) {
            case "MD5" :
                $isSgin =  (md5($prestr.$this->key) == $sign) ? true : false;
                break;
            default :
                $isSgin = false;
        }
        
        return $isSgin;
    }


    /**
     * 获取远程服务器ATN结果,验证返回URL
     * @param $notify_id 通知校验ID
     * @return 服务器ATN结果
     * 验证结果集：
     * invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空 
     * true 返回正确信息
     * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
     */
    private function getResponse($notify_id) {

        $transport   = strtolower($this->transport);
        $partner     = $this->partner;
        $veryfy_url  = '';
        if($transport == 'https') {
            $veryfy_url = $this->https_verify_url;
        }
        else {
            $veryfy_url = $this->http_verify_url;
        }
        $veryfy_url  = $veryfy_url."partner=" . $partner . "&notify_id=" . $notify_id;
        $responseTxt = $this->getHttpResponseGET($veryfy_url, $this->cacert);
        
        return $responseTxt;
    }

    /**
     * 远程获取数据，GET模式
     * 注意：
     * 1.使用Crul需要修改服务器中php.ini文件的设置，找到php_curl.dll去掉前面的";"就行了
     * 2.文件夹中cacert.pem是SSL证书请保证其路径有效，目前默认路径是：getcwd().'\\cacert.pem'
     * @param $url 指定URL完整路径地址
     * @param $cacert_url 指定当前工作目录绝对路径
     * return 远程输出的数据
     */
    private function getHttpResponseGET($url,$cacert_url) {
        $curl = curl_init($url);
        curl_setopt($curl, CURLOPT_HEADER, 0 ); // 过滤HTTP头
        curl_setopt($curl,CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);//SSL证书认证
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//严格认证
        curl_setopt($curl, CURLOPT_CAINFO,$cacert_url);//证书地址
        $responseText = curl_exec($curl);
        //var_dump( curl_error($curl) );//如果执行curl过程中出现异常，可打开此开关，以便查看异常内容
        curl_close($curl);
        
        return $responseText;
    }

} 